using System;
using System.Collections;
using System.Data;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.Policy;

namespace PHSRAG.Insight21.Personnel
{
    /// <summary>
    /// The AddEditDistribution class supports the AddEditDistribution.aspx page
    /// It allows the user to Edit or Add salary distribution
    /// The page takes care of adding and editing both internal and external salary distribution
    /// </summary>
    public partial class AddEditDistribution : NoCachePage
    {
        #region Constants

        private const string AddCommandName = "AddNew";
        private const string ClosePopupJSKey = "ClosePopup_7B7E6CD1-5D8C-47F1-B226-784226E4B8F7";
        private const string EditCommandName = "Edit";
        private const int EffectiveDateMaxValue = 15;
        private const int EffectiveDateMinValue = 1;
        private const int ExpirationDateMinValue = 14;
        private const string SplitIDKey = "SplitID_7B7E6CD1-5D8C-47F1-B226-784226E4B8F7";
        private const string TypeToggleJSKey = "ToggleJS_7B7E6CD1-5D8C-47F1-B226-784226E4B8F7";

        #endregion

        #region Enumerations (Private)

        //Enum to hold the column index for the external fund type table
        private enum FundTypeTableColumnIndex
        {
            Id,
            FundType
        }

        #endregion

        #region Instance variables (Private)

        private ICachePolicy cachePolicy;
        private ICachePolicy pageCachePolicy;
        private ViewStateCachePolicy viewStateCachePolicy;

        #endregion

        #region Private properties

        /// <summary>
        /// Get the Personnel details object from the cache
        /// Throw an exception if there is some error in retrieving the object from cache
        /// </summary>
        private PersonnelDetails PersonnelDetailsInstance
        {
            get
            {
                PersonnelDetails personnelDetails =
                    ResultsCache.GetObject(cachePolicy, PersonnelDetails.CacheKey) as PersonnelDetails;

                if (personnelDetails == null)
                {
                    throw new Exception
                        (@"Error in updating a salary distribution, failed to retrieve personnel details.");
                }

                return personnelDetails;
            }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handler for the event raised when the page is being loaded.
        /// Retrieve the split id from the pageCachePolicy and save it in viewSt5ate
        /// Databind all the controls on the page identifying the operation being carried out (Edit/Add)
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnPageLoad(object sender, EventArgs e)
        {
            annunciator.Message = string.Empty;
            annunciator.Title = string.Empty;

            if (!IsPostBack)
            {
                DataTable externalFundTypes = ExternalSalaryDistribution.
                    GetExternalFundSource(Utility.DataAccess.ConnectionString);

                fundSource.DataSource = externalFundTypes;
                fundSource.DataValueField = externalFundTypes.
                    Columns[(int) FundTypeTableColumnIndex.Id].ColumnName;
                fundSource.DataTextField = externalFundTypes.
                    Columns[(int) FundTypeTableColumnIndex.FundType].ColumnName;
                fundSource.DataBind();

                if (Request.
                    QueryString[Constants.QueryStringOperationKey].
                    Equals(Constants.UpdateOperation))
                {
                    Hashtable dictionary =
                        pageCachePolicy.GetCachedObject(Keys.IncomingDataKey) as Hashtable;

                    int splitID = Convert.ToInt32(Request.
                        QueryString[Constants.QueryStringSplitIDKey]);

                    txtTitle.Text = "Edit Distribution";
                    SalaryDistribution salaryDistrib =
                        PersonnelDetailsInstance.SalaryDistribs.GetSalaryDistribution(splitID);
                    DisplayDistribution(externalFundTypes, salaryDistrib, splitID);
                    saveDistribution.CommandName = EditCommandName;
                    viewStateCachePolicy.Cache(SplitIDKey, splitID);
                }
                else if (Request.
                    QueryString[Constants.QueryStringOperationKey].
                    Equals(Constants.AddOperation))
                {
                    internalSelect.Checked = true;
                    txtTitle.Text = "Add Distribution";
                    saveDistribution.CommandName = AddCommandName;
                }

                internalSelect.Attributes["onclick"] = @"javascript:DistributionTypeToggle('Internal');";
                externalSelect.Attributes["onclick"] = @"javascript:DistributionTypeToggle('External');";
                cancelSaveOperation.Attributes["onclick"] = @"javascript:window.close();";
            }
            SetDistributionDisplay();
        }

        /// <summary>
        /// Handler for the event raised when the save button is clicked on the screen
        /// Add a new distribution or update an existing distribution depending on the user input
        /// Remove the PersonnelDetailsInstance from the cache to enable the PersonnelDetails page to reload the data
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSave(object sender, EventArgs e)
        {
            try
            {
                UserContext userContext = (UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey);

                int splitID = Convert.ToInt32(Request.
                    QueryString[Constants.QueryStringSplitIDKey]);

                if (saveDistribution.CommandName.Equals(AddCommandName))
                {
                    GetNewDistribution().
                        AddDistribution(Utility.DataAccess.ConnectionString, userContext.DomainUserId);
                }
                else if (saveDistribution.CommandName.Equals(EditCommandName) && IsDistributionDirty(splitID))
                {
                    GetExistingDistribution().
                        UpdateDistribution(Utility.DataAccess.ConnectionString, userContext.DomainUserId);
                }

                ResultsCache.PurgeObject(cachePolicy);

                //This is used in the Per details page for tracking
                pageCachePolicy.Cache(Keys.TransactionFlagKey, true);

                ClientScript.RegisterStartupScript(GetType(), ClosePopupJSKey
                    , string.Format("<script>window.close(); window.opener.{0}.submit();</script>"
                        , Constants.PersonnelDetailsFormName));
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog
                    ("Add Edit Distribution", ex, true, Utility.ExceptionReport.ReportType.Error);
                annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
            }
        }

        #endregion

        #region Web Form Designer generated code

        /// <summary>
        /// Handler for the event raised when the page is being initialized.
        /// Instantiate the viewStateCachePolicy using the ViewState of the page.
        /// Retrieve the page cache policy in force.
        /// </summary>
        /// <param name="e">Argument associated with the event</param>
        protected override void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);

            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];
            viewStateCachePolicy = new PHSRAG.Insight21.Policy.ViewStateCachePolicy(ViewState);
        }

        /// <summary>
        /// Required method for Designer support 
        /// </summary>
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.OnPageLoad);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Databind the controls on the screen with the data from the SalaryDistribution object
        /// </summary>
        /// <param name="externalFundTypes"></param>
        /// <param name="distribution">SalaryDistribution instance</param>
        /// <param name="splitID"></param>
        private void DisplayDistribution(DataTable externalFundTypes, SalaryDistribution distribution, int splitID)
        {
            if (distribution is InternalSalaryDistribution)
            {
                internalSelect.Checked = true;
                fundNumber.Text =
                    ((InternalSalaryDistribution) distribution).FundNumber;
            }
            else
            {
                externalSelect.Checked = true;
                fundTitle.Text =
                    (distribution).Title;
                fundSource.SelectedValue = Convert.ToString(externalFundTypes.Select(String.Format("Name = '{0}'",
                    ((ExternalSalaryDistribution) distribution).ExternalFundSource))[0]["Id"]);
            }

            // format annual rate as currency but strip out "$" sign for validation
            annualRate.Text = string.Format("{0:c}", distribution.AnnualRate).Remove(0, 1);
            percentEffort.Text = Convert.ToString(distribution.PercentEffort);
            startDate.Prompt = string.Format("{0:MM/dd/yyyy}", distribution.Splits.GetDistributionSplit(splitID).EffectiveDate);
            endDate.Prompt = string.Format("{0:MM/dd/yyyy}", distribution.ExpirationDate);
            comments.Text = distribution.Comment;
        }

        /// <summary>
        /// Get a new distribution instance of type internal or external depending on the radio button selection
        /// </summary>
        /// <returns>SalaryDistribution instance</returns>
        private SalaryDistribution GetNewDistribution()
        {
            if (startDate.Selection == string.Empty || endDate.Selection == string.Empty)
            {
                throw new Exception(@"The start date and end date are required fields");
            }

            SalaryDistribution salaryDistrib = PersonnelDetailsInstance
                .SalaryDistribs.CreateNewSalaryDistribution((internalSelect.Checked)
                    ? SalaryDistributionType.Internal
                    : SalaryDistributionType.External);

            salaryDistrib.EmployeeJobMapID = Convert.ToInt32(Request.
                QueryString[Constants.QueryStringJobMapIDKey]);

            PopulateDistributionMembers(salaryDistrib);

            return salaryDistrib;
        }

        /// <summary>
        /// Get an existing distribution instance.
        /// Convert the type before returning if the radio button selection is changed
        /// </summary>
        /// <returns>SalaryDistribution instance</returns>
        private SalaryDistribution GetExistingDistribution()
        {
            SalaryDistribution salaryDistrib =
                PersonnelDetailsInstance.SalaryDistribs.GetSalaryDistribution
                    (Convert.ToInt32(viewStateCachePolicy.GetCachedObject(SplitIDKey)));

            if ((internalSelect.Checked && salaryDistrib is ExternalSalaryDistribution) ||
                (externalSelect.Checked && salaryDistrib is InternalSalaryDistribution))
            {
                salaryDistrib = SalaryDistributions.ConvertDistributionType(salaryDistrib);
            }

            PopulateDistributionMembers(salaryDistrib);
            return salaryDistrib;
        }

        /// <summary>
        /// Populate the distribution members with the information entered by the user
        /// </summary>
        private bool IsDistributionDirty(int splitID)
        {
            //get the cached distrib
            SalaryDistribution salaryDistrib =
                PersonnelDetailsInstance.SalaryDistribs.GetSalaryDistribution
                    (Convert.ToInt32(viewStateCachePolicy.GetCachedObject(SplitIDKey)));

            if (salaryDistrib is InternalSalaryDistribution)
            {
                if (externalSelect.Checked ||
                    ((InternalSalaryDistribution) salaryDistrib).FundNumber != fundNumber.Text)
                {
                    return true;
                }
            }
            else
            {
                if (internalSelect.Checked ||
                    (salaryDistrib).Title != fundTitle.Text ||
                        ((ExternalSalaryDistribution) salaryDistrib).ExternalFundSource != fundSource.SelectedItem.Text)
                {
                    return true;
                }
            }

            DateTime startDateFromControl = (startDate.Selection == string.Empty) ?
                                                                                      Convert.ToDateTime(startDate.Prompt) : Convert.ToDateTime(startDate.Selection);
            DateTime endDateFromControl = (endDate.Selection == string.Empty) ?
                                                                                  Convert.ToDateTime(endDate.Prompt) : Convert.ToDateTime(endDate.Selection);

            if (salaryDistrib.AnnualRate != Convert.ToDecimal(annualRate.Text) ||
                salaryDistrib.PercentEffort != Convert.ToDecimal(percentEffort.Text) ||
                    salaryDistrib.Comment != comments.Text ||
                        salaryDistrib.Splits.GetDistributionSplit(splitID).EffectiveDate != startDateFromControl.ToString("MM/dd/yyyy") ||
                            salaryDistrib.ExpirationDate != endDateFromControl.ToString("MM/dd/yyyy"))
            {
                return true;
            }

            return false;
        }


        /// <summary>
        /// Populate the distribution members with the information entered by the user
        /// </summary>
        /// <param name="salaryDistrib">SalaryDistribution object</param>
        private void PopulateDistributionMembers(SalaryDistribution salaryDistrib)
        {
            if (salaryDistrib is InternalSalaryDistribution)
            {
                if (fundNumber.Text == string.Empty)
                {
                    throw new Exception(@"Fund number is a required field");
                }
                
                ((InternalSalaryDistribution) salaryDistrib).FundNumber = fundNumber.Text;
            }
            else
            {
                (salaryDistrib).Title = fundTitle.Text;
                ((ExternalSalaryDistribution) salaryDistrib).ExternalFundSource = fundSource.SelectedItem.Text;
            }
            salaryDistrib.AnnualRate = Convert.ToDecimal(annualRate.Text);
            salaryDistrib.PercentEffort = Convert.ToDecimal(percentEffort.Text);
            salaryDistrib.Comment = comments.Text;
            salaryDistrib.EditedBy = ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).Username;
            salaryDistrib.EditDate = DateTime.Now.ToShortDateString();

            DateTime startDateFromControl = (startDate.Selection == string.Empty) ?
                                                                                      Convert.ToDateTime(startDate.Prompt) : Convert.ToDateTime(startDate.Selection);
            DateTime endDateFromControl = (endDate.Selection == string.Empty) ?
                                                                                  Convert.ToDateTime(endDate.Prompt) : Convert.ToDateTime(endDate.Selection);

            if (startDateFromControl > endDateFromControl)
            {
                throw new Exception(@"Start date cannot be greater than the end date");
            }
            if (endDateFromControl < startDateFromControl)
            {
                throw new Exception(@"End date connot be less than the start date");
            }

            salaryDistrib.EffectiveDate = startDateFromControl.ToString("MM/dd/yyyy");
            salaryDistrib.ExpirationDate = endDateFromControl.ToString("MM/dd/yyyy");
        }

        /// <summary>
        /// Set the visibility of the controls on the page depending on the type of the distribution
        /// Register a start up script to call the already embedded JS
        /// </summary>
        private void SetDistributionDisplay()
        {
            string script;
            if (internalSelect.Checked)
            {
                script = @"<script>
				internalDistribution.style.visibility='visible';
				internalDistribution.style.display='inline';
				externalDistribution.style.visibility='hidden';
				externalDistribution.style.display='none';
				</script>";
            }
            else
            {
                script = @"<script>
				externalDistribution.style.visibility='visible';
				externalDistribution.style.display='inline';
				internalDistribution.style.visibility='hidden';
				internalDistribution.style.display='none';
				</script>";
            }
            Page.ClientScript.RegisterStartupScript(GetType(), TypeToggleJSKey, script);
        }

        #endregion
    }
}